package dao

import (
	"context"
	"wxpay/internal/model"
	"wxpay/pkg/merrors"

	"github.com/nsqio/go-nsq"
	"github.com/patrickmn/go-cache"
	"gorm.io/gorm"
)

type Refund interface {
	// 创建退款
	Create(ctx context.Context, tx *gorm.DB, m *model.Refund) error
	// 根据商户退款单号查询退款
	QueryByOutRefundNo(ctx context.Context, db *gorm.DB, outRefundNo string) (refund *model.Refund, err error)
	// 退款成功
	Success(ctx context.Context, tx *gorm.DB, m *model.Refund) error
	// 退款关闭
	Close(ctx context.Context, tx *gorm.DB, m *model.Refund) error
	// 退款异常
	Abnormal(ctx context.Context, tx *gorm.DB, m *model.Refund) error
	// 退款处理中
	Processing(ctx context.Context, tx *gorm.DB, m *model.Refund) error
	// 根据商户交易号查询已退款列表
	QueryRefundedByOutTradeNo(ctx context.Context, db *gorm.DB, outTradeNo string) (refunds []*model.Refund, err error)
}

type RefundRepo struct {
	*gorm.DB
	producer *nsq.Producer
	cache    *cache.Cache
}

func NewRefundRepo(d *gorm.DB, p *nsq.Producer, c *cache.Cache) *RefundRepo {
	return &RefundRepo{
		DB:       d,
		producer: p,
		cache:    c,
	}
}

// 创建退款
func (r RefundRepo) Create(_ context.Context, tx *gorm.DB, m *model.Refund) error {
	const op merrors.Op = "RefundRepo.Create"
	if e := tx.Create(m).Error; e != nil {
		return merrors.E(op, merrors.DBError, e, merrors.ErrInternal)
	}
	return nil
}

func (r RefundRepo) QueryByOutRefundNo(_ context.Context, db *gorm.DB, outRefundNo string) (refund *model.Refund, err error) {
	const op merrors.Op = "RefundRepo.QueryByOutRefundNo"
	res := model.Refund{}
	if e := db.Model(&model.Refund{}).Where("out_refund_no = ?", outRefundNo).First(&res).Error; e != nil {
		if e == gorm.ErrRecordNotFound {
			err = merrors.E(op, merrors.NotExist, merrors.ErrNotFound, e)
		} else {
			err = merrors.E(op, merrors.DBError, merrors.ErrInternal, e)
		}
		return
	}
	refund = &res
	return
}

// 退款成功
func (r RefundRepo) Success(_ context.Context, tx *gorm.DB, m *model.Refund) error {
	const op merrors.Op = "RefundRepo.Success"
	if e := tx.Model(m).Updates(map[string]interface{}{
		"amount_info":           m.GetRefundAmount().ToJson(),
		"user_received_account": m.UserReceivedAccount,
		"status":                m.Status,
		"success_time":          m.SuccessTime,
		"update_time":           m.UpdateTime,
		"extra":                 m.Extra,
	}).Where("id = ?", m.ID).Error; e != nil {
		return merrors.E(op, merrors.DBError, e, merrors.ErrInternal)
	}
	return nil
}

// 退款关闭
func (r RefundRepo) Close(_ context.Context, tx *gorm.DB, m *model.Refund) error {
	const op merrors.Op = "RefundRepo.Close"
	if e := tx.Model(m).Updates(map[string]interface{}{
		"user_received_account": m.UserReceivedAccount,
		"status":                m.Status,
		"update_time":           m.UpdateTime,
	}).Where("id = ?", m.ID).Error; e != nil {
		return merrors.E(op, merrors.DBError, e, merrors.ErrInternal)
	}
	return nil
}

// 退款异常
func (r RefundRepo) Abnormal(_ context.Context, tx *gorm.DB, m *model.Refund) error {
	const op merrors.Op = "RefundRepo.Abnormal"
	if e := tx.Model(m).Updates(map[string]interface{}{
		"user_received_account": m.UserReceivedAccount,
		"status":                m.Status,
		"update_time":           m.UpdateTime,
	}).Where("id = ?", m.ID).Error; e != nil {
		return merrors.E(op, merrors.DBError, e, merrors.ErrInternal)
	}
	return nil
}

// 退款处理中
func (r RefundRepo) Processing(_ context.Context, tx *gorm.DB, m *model.Refund) error {
	const op merrors.Op = "RefundRepo.Processing"
	if e := tx.Model(m).Updates(map[string]interface{}{
		"user_received_account": m.UserReceivedAccount,
		"status":                m.Status,
		"update_time":           m.UpdateTime,
	}).Where("id = ?", m.ID).Error; e != nil {
		return merrors.E(op, merrors.DBError, e, merrors.ErrInternal)
	}
	return nil
}

// 根据交易订单号查询已成功退款列表
func (r RefundRepo) QueryRefundedByOutTradeNo(ctx context.Context, db *gorm.DB, outTradeNo string) (refunds []*model.Refund, err error) {
	const op merrors.Op = "RefundRepo.QueryRefundedByOutTradeNo"
	if e := db.Model(&model.Refund{}).Where("out_trade_no = ? and status = ?", outTradeNo, model.RefundStatusSuccess).Find(&refunds).Error; e != nil {
		err = merrors.E(op, merrors.DBError, merrors.ErrInternal, e)
		return
	}
	return
}
